﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Drawing.Imaging;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace BaseUi
{
    /// <summary>
    /// 警告信息容器
    /// </summary>
    public class WarnInfoContainer
    {
        ConcurrentDictionary<WarnInfoItem, string> InfoAll = new ConcurrentDictionary<WarnInfoItem, string>();

        /// <summary>
        /// 有新的日志加入或移除时，1，当前全部，2，加入的，3移除的
        /// </summary>
        public Action<IEnumerable<WarnInfoItem>, IEnumerable<WarnInfoItem>, IEnumerable<WarnInfoItem>> WarnInfoChanged;

        /// <summary>
        /// 添加或者更新信息
        /// </summary>
        public void AddOrUpdate(IEnumerable<WarnInfoItem> warnInfos)
        {
            if (warnInfos == null || !warnInfos.Any())
                return;

            List<WarnInfoItem> adds = new List<WarnInfoItem>();
            List<WarnInfoItem> cles = new List<WarnInfoItem>();
            var info1 = warnInfos.Where(o => o.WarnType == WarnInfoType.WhileWarn).GroupBy(o => o.Source);
            var info2 = warnInfos.Where(o => o.WarnType == WarnInfoType.AlwayWarn);
            DateTime dt = DateTime.Now;

            foreach (var addText in info1)
            {
                var yuan = InfoAll.Keys.Where(o => o.WarnType == WarnInfoType.WhileWarn && o.Source == addText.Key);
                var uni = yuan.Intersect(addText);
                var add = addText.Except(yuan).Except(uni);
                var cle = yuan.Except(addText).Except(uni);
                adds.AddRange(add);
                cles.AddRange(cle);

                foreach (var item in cle)
                {
                    item.TimeTo = item.TimeTo.HasValue ? item.TimeTo : dt;
                    if (!InfoAll.TryRemove(item, out string val))
                        InfoAll.TryRemove(item, out string val1);
                }
                foreach (var item in add)
                {
                    item.Id = Guid.NewGuid().ToString("N");
                    item.TimeGo = item.TimeGo == new DateTime() ? dt : item.TimeGo;
                    if (!InfoAll.TryAdd(item, null))
                        InfoAll.TryAdd(item, null);
                }
            }

            {
                var exc = info2.Except(InfoAll.Keys);
                foreach (var item in exc)
                {
                    item.Id = Guid.NewGuid().ToString("N");
                    item.TimeGo = item.TimeGo == new DateTime() ? dt : item.TimeGo;
                    if (!InfoAll.TryAdd(item, null))
                        InfoAll.TryAdd(item, null);
                    adds.Add(item);
                }
            }

            if (adds.Any() || cles.Any())
                WarnInfoChanged?.BeginInvoke(InfoAll.Keys.ToArray(), adds, cles, null, null);
        }

        /// <summary>
        /// 更新循环警告
        /// </summary>
        public void UpdateWhileWarn(IEnumerable<string> texts)
        {
            UpdateWhileWarn(string.Empty, texts);
        }

        /// <summary>
        /// 更新循环警告
        /// </summary>
        public void UpdateWhileWarn(string source, IEnumerable<string> texts)
        {
            DateTime dt = DateTime.Now;
            AddOrUpdate(texts.Select(o => new WarnInfoItem()
            {
                Source = source,
                Text = o,
                WarnType = WarnInfoType.WhileWarn,
                TimeGo = dt,
                TimeTo = null
            }));
        }

        /// <summary>
        /// 添加常驻警告
        /// </summary>
        public void AddAlwayWarn(string text)
        {
            AddAlwayWarn(string.Empty, new string[] { text });
        }

        /// <summary>
        /// 添加常驻警告
        /// </summary>
        public void AddAlwayWarn(IEnumerable<string> texts)
        {
            AddAlwayWarn(string.Empty, texts);
        }

        /// <summary>
        /// 添加常驻警告
        /// </summary>>
        public void AddAlwayWarn(string source, string text)
        {
            AddAlwayWarn(source, new string[] { text });
        }

        /// <summary>
        /// 添加常驻警告
        /// </summary>>
        public void AddAlwayWarn(string source, IEnumerable<string> texts)
        {
            DateTime dt = DateTime.Now;
            AddOrUpdate(texts.Select(o => new WarnInfoItem()
            {
                Source = source,
                Text = o,
                WarnType = WarnInfoType.AlwayWarn,
                TimeGo = dt,
                TimeTo = null
            }));
        }

        /// <summary>
        /// 移除警告信息
        /// </summary>
        public void RemoveAll()
        {
            RemoveAll(null);
        }

        /// <summary>
        /// 移除警告信息
        /// </summary>
        public void RemoveAll(WarnInfoType errType)
        {
            RemoveAll(null, errType);
        }

        /// <summary>
        /// 移除警告信息
        /// </summary>
        public void RemoveAll(string source, WarnInfoType? errType = null)
        {
            if (InfoAll.IsEmpty)
                return;

            var clear = InfoAll.Keys
                .Where(o => (source == null ? true : o.Source == source) && (errType.HasValue ? o.WarnType == errType.Value : true));

            if (clear == null || !clear.Any())
                return;

            var dt = DateTime.Now;
            foreach (var item in clear)
            {
                item.TimeTo = item.TimeTo.HasValue ? item.TimeTo : dt;
                if (!InfoAll.TryRemove(item, out string val))
                    InfoAll.TryRemove(item, out string val1);
            }

            WarnInfoChanged?.BeginInvoke(InfoAll.Keys, new List<WarnInfoItem>(), clear, null, null);
        }
    }

    /// <summary>
    /// 警告信息
    /// </summary>
    public class WarnInfoItem
    {
        /// <summary>
        /// 唯一编码
        /// </summary>
        public string Id { get; set; }
        /// <summary>
        /// 来源
        /// </summary>
        public string Source { get; set; }
        /// <summary>
        /// 文本信息
        /// </summary>
        public string Text { get; set; }
        /// <summary>
        /// 级别（提示、警告、错误、致命）
        /// </summary>
        public string Level { get; set; }
        /// <summary>
        /// 解决方案
        /// </summary>
        public string Solution { get; set; }
        /// <summary>
        /// 警告类型
        /// </summary>
        public WarnInfoType WarnType { get; set; }
        /// <summary>
        /// 开始时间
        /// </summary>
        public DateTime TimeGo { get; set; }
        /// <summary>
        /// 结束时间
        /// </summary>
        public DateTime? TimeTo { get; set; }
        /// <summary>
        /// 自定义信息
        /// </summary>
        public object Tag { get; set; }

        public override bool Equals(object obj)
        {
            if (obj is WarnInfoItem b)
                return (Source == b.Source && Text == b.Text && Level == b.Level && Solution == b.Solution && WarnType == b.WarnType);
            else
                return false;
        }

        public override int GetHashCode()
        {
            return (Source + Text + Level + Solution + WarnType.ToString()).GetHashCode();
        }

        public override string ToString()
        {
            return $"{(string.IsNullOrEmpty(Source) ? "" : $"[{Source}]")}{(string.IsNullOrEmpty(Level) ? "" : $"[{Level}]")}{Text}{(string.IsNullOrEmpty(Solution) ? "" : $"({Solution})")}{(TimeTo.HasValue ? $"({(TimeTo.Value - TimeGo).TotalSeconds.ToString("0.00")}s)" : "")}";
        }

        public static bool operator ==(WarnInfoItem a, WarnInfoItem b)
        {
            return a.Equals(b);
        }

        public static bool operator !=(WarnInfoItem a, WarnInfoItem b)
        {
            return !a.Equals(b);
        }
    }

    /// <summary>
    /// 警告信息类型
    /// </summary>
    public enum WarnInfoType
    {
        /// <summary>
        /// 常驻错误。加入一次需要手动清除
        /// </summary>
        AlwayWarn = 10,
        /// <summary>
        /// 循环错误。每次需要把全部的错误参入进来
        /// </summary>
        WhileWarn = 20,
    }
}
